package com.devframe.web.struts2;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;


import com.devframe.web.MyStatics;
import com.devframe.web.struts2.result.MyActionResult;
import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.DefaultActionInvocation;
import com.opensymphony.xwork2.DefaultActionProxy;
import com.opensymphony.xwork2.Result;
import com.opensymphony.xwork2.XWorkException;
import com.opensymphony.xwork2.config.ConfigurationException;
import com.opensymphony.xwork2.config.entities.ActionConfig;
import com.opensymphony.xwork2.config.entities.InterceptorMapping;
import com.opensymphony.xwork2.config.entities.ResultConfig;
import com.opensymphony.xwork2.interceptor.PreResultListener;
import com.opensymphony.xwork2.util.location.LocationImpl;
import com.opensymphony.xwork2.util.logging.Logger;
import com.opensymphony.xwork2.util.logging.LoggerFactory;
import com.opensymphony.xwork2.util.profiling.UtilTimerStack;
import java.util.concurrent.ConcurrentHashMap;
public class MyActionInvocation extends DefaultActionInvocation {
	private static final Logger LOG = LoggerFactory.getLogger(MyActionInvocation.class);
	private static final Class[] EMPTY_CLASS_ARRAY = new Class[0];
	   //动态创建的action result
	private static Map<String,LinkedHashMap<String,ResultConfig>> ACTION_RESULT_CONFIG_MAP=new LinkedHashMap<String,LinkedHashMap<String,ResultConfig>>();
	   
	   
	public MyActionInvocation(Map<String, Object> extraContext,boolean pushAction) {
		super(extraContext, pushAction);
	}
	private synchronized ResultConfig getRecultConfig(){
    	String actionClassKey=super.getAction().getClass().getName();
    	LinkedHashMap<String,ResultConfig> actionMap=MyActionInvocation.ACTION_RESULT_CONFIG_MAP.get(actionClassKey);
    	if(actionMap==null){
    		actionMap=new LinkedHashMap<String, ResultConfig>();
    		MyActionInvocation.ACTION_RESULT_CONFIG_MAP.put(actionClassKey, actionMap);
    	}else{
    		actionMap=MyActionInvocation.ACTION_RESULT_CONFIG_MAP.get(actionClassKey);
    	}
    	String resultKey=super.getProxy().getMethod();
    	ResultConfig resultConfig=actionMap.get(resultKey);
    	
    	if(resultConfig==null){
    		Class returnType=(Class)super.getStack().findValue(MyStatics.ACTION_RESULT_TYPE);
    		String resultVal="";	
    		if(returnType==void.class){
    			resultVal="";
    			
    		}else{
    			Object obj=super.getStack().findValue(MyStatics.ACTION_RESULT_VAL);
    			if(obj!=null){
    				resultVal=obj.toString();
    			}
    		}
    		resultConfig=new ResultConfig.Builder(resultCode,MyActionResult.class.getName()).location(new LocationImpl("",resultCode+".jsp")).build();
    		actionMap.put(resultKey, resultConfig);
    	}
    	return resultConfig;
	}
	
	 public Result createResult() throws Exception {
	        if (explicitResult != null) {
	            Result ret = explicitResult;
	            explicitResult = null;
	            return ret;
	        }
	        ActionConfig config = proxy.getConfig();
	        Map<String, ResultConfig> results = config.getResults();
	        ResultConfig resultConfig = null;
	        
	        Class returnType=(Class)super.getStack().findValue(MyStatics.ACTION_RESULT_TYPE);
	       //不为空并且为字符串的情况下 并且 返回值和
	        if((returnType!=void.class && returnType==String.class) || !Action.NONE.equals(resultCode)){
		        try {
		            resultConfig = results.get(resultCode);
		           // System.out.println("--resultConfig-"+resultConfig);
		        } catch (NullPointerException e) {
		            // swallow
		        }
		        if (resultConfig == null) {
		            resultConfig = results.get("*");
		        }
	        }else { 
	        	resultConfig=getRecultConfig();
	        } 

	        if (resultConfig != null) {
	            try {
	                return objectFactory.buildResult(resultConfig, invocationContext.getContextMap());
	            } catch (Exception e) {
	                LOG.error("There was an exception while instantiating the result of type " + resultConfig.getClassName(), e);
	                throw new XWorkException(e, resultConfig);
	            }
	        } else if (resultCode != null && !Action.NONE.equals(resultCode) && unknownHandlerManager.hasUnknownHandlers()) {
	            return unknownHandlerManager.handleUnknownResult(invocationContext, proxy.getActionName(), proxy.getConfig(), resultCode);
	        }
	        return null;
	    }
	
	 protected String invokeAction(Object action, ActionConfig actionConfig) throws Exception {
	        String methodName = proxy.getMethod();
	        if (LOG.isDebugEnabled()) {
	            LOG.debug("Executing action method = " + actionConfig.getMethodName());
	        }
	        String timerKey = "invokeAction: " + proxy.getActionName();
	        try {
	            UtilTimerStack.push(timerKey);
	            boolean methodCalled = false;
	            Object methodResult = null;
	            Method method = null;
	            try {
	                method = getAction().getClass().getMethod(methodName, EMPTY_CLASS_ARRAY);
	            } catch (NoSuchMethodException e) {
	                try {
	                    String altMethodName = "do" + methodName.substring(0, 1).toUpperCase() + methodName.substring(1);
	                    method = getAction().getClass().getMethod(altMethodName, EMPTY_CLASS_ARRAY);
	                } catch (NoSuchMethodException e1) {
	                    if (unknownHandlerManager.hasUnknownHandlers()) {
	                        try {
	                            methodResult = unknownHandlerManager.handleUnknownMethod(action, methodName);
	                            methodCalled = true;
	                        } catch (NoSuchMethodException e2) {
	                            throw e;
	                        }
	                    } else {
	                        throw e;
	                    }
	                }
	            }
	            if (!methodCalled) {
	                methodResult = method.invoke(action, new Object[0]);
	            }
	            Class returnType=method.getReturnType();
	            //保存返回值类型
	            super.getStack().set(MyStatics.ACTION_RESULT_TYPE, returnType);
	            //非字符串类型 、VOID 的情况下保存方法的返回值
	            super.getStack().set(MyStatics.ACTION_RESULT_VAL, methodResult);
	            //空值类型 
	            if(returnType==void.class){
	            	return Action.NONE;
	            }
	            if (methodResult instanceof Result) {
	                this.explicitResult = (Result) methodResult;
	                container.inject(explicitResult);
	                return null;
	            } else  if(methodResult!=null && methodResult instanceof String && !methodResult.toString().contains(MyStatics.REDIRECT_ACTION_RESULT_PREFIX)){
	            	//字符串的形式,但不含redirect:
	            	return (String)methodResult;
	            }else{
	            	 return  Action.NONE;
	            }
	           
	        } catch (NoSuchMethodException e) {
	            throw new IllegalArgumentException("The " + methodName + "() is not defined in action " + getAction().getClass() + "");
	        } catch (InvocationTargetException e) {
	            // We try to return the source exception.
	            Throwable t = e.getTargetException();
	            if (actionEventListener != null) {
	                String result = actionEventListener.handleException(t, getStack());
	                if (result != null) {
	                    return result;
	                }
	            }
	            if (t instanceof Exception) {
	                throw (Exception) t;
	            } else {
	                throw e;
	            }
	        } finally {
	            UtilTimerStack.pop(timerKey);
	        }
	    }
	 private void executeResult() throws Exception {
	        result = createResult();
	        String timerKey = "executeResult: " + getResultCode();
	        try {
	            UtilTimerStack.push(timerKey);
	            if (result != null) {
	                result.execute(this);
	            } else if (resultCode != null && !Action.NONE.equals(resultCode)) {
	                throw new ConfigurationException("No result defined for action " + getAction().getClass().getName()
	                        + " and result " + getResultCode(), proxy.getConfig());
	            } else {
	                if (LOG.isDebugEnabled()) {
	                    LOG.debug("No result returned for action " + getAction().getClass().getName() + " at " + proxy.getConfig().getLocation());
	                }
	            }
	        } finally {
	            UtilTimerStack.pop(timerKey);
	        }
	    }
	 public String invoke() throws Exception {
	        String profileKey = "invoke: ";
	        try {
	            UtilTimerStack.push(profileKey);

	            if (executed) {
	                throw new IllegalStateException("Action has already executed");
	            }

	            if (interceptors.hasNext()) {
	                final InterceptorMapping interceptor = (InterceptorMapping) interceptors.next();
	                String interceptorMsg = "interceptor: " + interceptor.getName();
	                UtilTimerStack.push(interceptorMsg);
	                try {
	                      resultCode = interceptor.getInterceptor().intercept(MyActionInvocation.this);
	                     }
	                finally {
	                    UtilTimerStack.pop(interceptorMsg);
	                }
	            } else {
	                resultCode = invokeActionOnly();
	            }

	            if (!executed) {
	                if (preResultListeners != null) {
	                    for (Object preResultListener : preResultListeners) {
	                        PreResultListener listener = (PreResultListener) preResultListener;

	                        String _profileKey = "preResultListener: ";
	                        try {
	                            UtilTimerStack.push(_profileKey);
	                            listener.beforeResult(this, resultCode);
	                        }
	                        finally {
	                            UtilTimerStack.pop(_profileKey);
	                        }
	                    }
	                }

	                if (proxy.getExecuteResult()) {
	                   executeResult();
	                }

	                executed = true;
	            }

	            return resultCode;
	        }
	        finally {
	            UtilTimerStack.pop(profileKey);
	        }
	    }
}
